package belf.migrate.api.sink.jdbc.helper;

import belf.migrate.api.table.schema.ColumnMetaData;
import belf.migrate.api.taskconf.ColumnRule;
import belf.migrate.api.taskconf.ColumnSyncConf;

import java.util.*;


// 使用前需要putTableColumnMetaData & setMapping
public class ColumnRuleHelper {
    private final Map<String, Map<String, ColumnMetaData>> meta = new HashMap<>();  // 字段元信息，tableName -> columnName -> ColumnMetaData
    private Map<String, List<ColumnSyncConf>> mapping = new HashMap<>();  // 字段规则映射，tableName -> List<ColumnSyncConf>

    private static final Map<String, String> select_column_list_cache = new HashMap<>();  // tableName -> 字符串: "columnName, FUNCTION AS XXX, ..."
    private static final Map<String, String> placeholder_cache = new HashMap<>();  // tableName -> 字符串: "?, FUNCTION(?), ..."

    private static final Map<String, String> read_groovy_cache = new HashMap<>();  // tableName+columnName -> read groovy script
    private static final Map<String, String> write_groovy_cache = new HashMap<>();  // tableName+columnName -> write groovy script

    /*
    private ColumnRuleHelper() {
    }

    // 利用类加载机制来实现线程安全和懒加载
    private static class ColumnRuleHelperHolder {
        private static final ColumnRuleHelper INSTANCE = new ColumnRuleHelper();
    }

    // 单例
    public static ColumnRuleHelper getInstance() {
        return ColumnRuleHelperHolder.INSTANCE;
    }
    */

    public ColumnRuleHelper(Map<String, List<ColumnSyncConf>> mapping) {
        setMapping(mapping);
    }

    public void putTableColumnMetaData(String tableName, Map<String, ColumnMetaData> columnMetaDataMap) {
        this.meta.put(tableName, columnMetaDataMap);
    }

    public void setMapping(Map<String, List<ColumnSyncConf>> mapping) {
        this.mapping = mapping;
        mapping.forEach(((tableName, columnSyncConfList) -> columnSyncConfList.forEach((columnSyncConf -> {
            ColumnRule readRule = columnSyncConf.getReadRule();
            ColumnRule writeRule = columnSyncConf.getWriteRule();
            if (null != readRule && readRule.getRuleType() == ColumnRule.RuleType.SCRIPT_GROOVY) {
                read_groovy_cache.put(tableName + columnSyncConf.getColumnName(), readRule.getRuleContent());
            }
            if (null != writeRule && writeRule.getRuleType() == ColumnRule.RuleType.SCRIPT_GROOVY) {
                write_groovy_cache.put(tableName + columnSyncConf.getColumnName(), writeRule.getRuleContent());
            }
        }))));
    }

    // 根据表的字段元信息、字段规则读规则配置 生成SELECT SQL中的逗号分割的字段列表，默认为字段名，或FUNCTION类型的读取规则配置
    public String generate_select_column_list(String tableName) {
        if (select_column_list_cache.containsKey(tableName)) {
            return select_column_list_cache.get(tableName);
        }

        List<String> select_column_list = new ArrayList<>();

        Map<String, String> replaceColumns = new HashMap<>();  // 需要替换的字段 columnName -> "FUNCTION AS XXX"
        List<ColumnSyncConf> columnSyncConfList = mapping.get(tableName);
        if (null != columnSyncConfList) {
            for (ColumnSyncConf columnSyncConf : columnSyncConfList) {
                ColumnRule columnRule = columnSyncConf.getReadRule();
                if (ColumnRule.RuleType.FUNCTION.equals(columnRule.getRuleType())) {
                    replaceColumns.put(columnSyncConf.getColumnName(), columnRule.getRuleContent());
                }
            }
        }

        Map<String, ColumnMetaData> columnMetaDataMap = meta.get(tableName);
        if (null != columnMetaDataMap) {
            for (String columnName : columnMetaDataMap.keySet()) {
                select_column_list.add(replaceColumns.getOrDefault(columnName, columnName));
            }
        }

        String select_column_list_str = String.join(", ", select_column_list);
        select_column_list_cache.put(tableName, select_column_list_str);
        return select_column_list_str;
    }

    // 根据表名、字段名查询INSERT SQL PreparedStatement中的占位符，默认为?，或FUNCTION类型的写入规则配置
    public String get_placeholder(String tableName, String columnName) {
        String key = tableName + columnName;
        if (placeholder_cache.containsKey(key)) {
            return placeholder_cache.get(key);
        }

        String placeholder = "?";
        List<ColumnSyncConf> columnSyncConfList = mapping.get(tableName);
        if (null != columnSyncConfList) {
            for (ColumnSyncConf columnSyncConf : columnSyncConfList) {
                if (Objects.equals(columnSyncConf.getColumnName(), columnName)) {
                    ColumnRule columnRule = columnSyncConf.getWriteRule();
                    if (ColumnRule.RuleType.FUNCTION.equals(columnRule.getRuleType())) {
                        placeholder = columnRule.getRuleContent();
                    }
                }
            }
        }
        placeholder_cache.put(key, placeholder);
        return placeholder;
    }

    // 应用GROOVY脚本的读取规则
    public Object apply_read_rule(String tableName, String columnName, Object columnValue) {
        String groovy_script = read_groovy_cache.get(tableName + columnName);
        if (Objects.nonNull(groovy_script)) {
            return GroovyScriptExecutor.executeScript(groovy_script, Map.of("value", columnValue));
        } else {
            return columnValue;
        }
    }

    // 应用GROOVY脚本的写入规则
    public Object apply_write_rule(String tableName, String columnName, Object columnValue) {
        String groovy_script = write_groovy_cache.get(tableName + columnName);
        if (Objects.nonNull(groovy_script)) {
            return GroovyScriptExecutor.executeScript(groovy_script, Map.of("value", columnValue));
        } else {
            return columnValue;
        }
    }

    public boolean needConvert(String tableName, String columnName) {
        List<ColumnSyncConf> columnSyncConfList = mapping.get(tableName);
        if (null != columnSyncConfList) {
            for (ColumnSyncConf columnSyncConf : columnSyncConfList) {
                if (columnSyncConf.getColumnName().equalsIgnoreCase(columnName)) {
                    return true;
                }
            }
        }
        return false;
    }

}